package com.lzq.chatroom_project.common.utils;

import com.github.benmanes.caffeine.cache.Cache;
import com.lzq.chatroom_project.common.config.redis_caffeine_config.CacheFrontendContext;
import com.lzq.chatroom_project.common.config.redis_caffeine_config.TryConnectTime;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.concurrent.TimeUnit;


@Slf4j
@Configuration
public class CacheUtil {

    @Autowired
    private CacheFrontendContext cacheFrontendContext;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TryConnectTime tryConnectTime;
    @Qualifier("waitingSyncDataCache")
    private Cache waitingSyncDataCache;

    private boolean setStringByCaffeine(String key, String value) {
        try {
            cacheFrontendContext.getCache().put(key, value);
            return true;
        } catch (Exception e) {
            log.error("CacheUtil error, setByCaffeine({}, {})", key, value, e);
            return false;
        }
    }

    private boolean setStringByRedis(String key, String value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            log.error("CacheUtil error, setByRedis({}, {})", key, value, e);
            return false;
        }
    }

    private boolean setStringByRedis(String key, String value, Long time) {
        try {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            log.error("CacheUtil error, setByRedis({}, {})", key, value, e);
            return false;
        }
    }

    private boolean delByCaffeine(String... key) {
        try {
            if (key.length == 1) {
                cacheFrontendContext.getCache().invalidate(key[0]);
            } else {
                cacheFrontendContext.getCache().invalidateAll(
                        (Collection<String>) CollectionUtils.arrayToList(key).iterator()
                );
            }
            return true;
        } catch (Exception e) {
            log.error("CacheUtil error, delByCaffeine({})", key, e);
            return false;
        }
    }

    private boolean delByRedis(String... key) {
        try {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(
                        (Collection<String>) CollectionUtils.arrayToList(key)
                );
            }
            return true;
        } catch (Exception e) {
            log.error("CacheUtil error, delByRedis({}, {})", key, e);
            return false;
        }
    }

    /**
     * 设置值
     * @param key
     * @param value
     * @return
     */
    public boolean set(String key, String value) {
        try {
            if (tryConnectTime.isDegrade()) {
                waitingSyncDataCache.put(key, value);
                return setStringByCaffeine(key, value);
            }
            return setStringByRedis(key, value);
        } catch (Exception e) {
            log.error("CacheUtil error, set({}, {}), isDegrade:{}", key, value, e, tryConnectTime.isDegrade());
            return false;
        }
    }

    /**
     * 设置值(设置过期时间)
     * @param key
     * @param value
     * @param time 单位: 秒
     * @return
     */
    public boolean set(String key, String value, Long time) {
        try {
            if (tryConnectTime.isDegrade()) {
                waitingSyncDataCache.put(key, value);
                return setStringByCaffeine(key, value);
            }
            return setStringByRedis(key, value, time);
        } catch (Exception e) {
            log.error("CacheUtil error, set({}, {}), isDegrade:{}", key, value, e, tryConnectTime.isDegrade());
            return false;
        }
    }

    /**
     * 获取值
     * @param key
     * @return
     */
    public String get(String key) {
        return cacheFrontendContext.getCacheFrontend().get(key).toString();
    }

    /**
     * 删除值
     * @param key
     * @return
     */
    public boolean del(String... key) {
        try {
            if (null != key && key.length > 0) {
                if (tryConnectTime.isDegrade()) {
                    waitingSyncDataCache.invalidate(key);
                    return delByCaffeine(key);
                }
                return delByRedis(key);
            }
            return true;
        } catch (Exception e) {
            log.error("CacheUtil error, del({})", key, e);
            return false;
        }
    }

    /**
     * 判断key是否存在
     * @param key
     * @return
     * 规定:Redis有的数据Caffeine中必定有
     */
    public boolean hasKey(String key) {
        try {
            if (StringUtils.hasText(key)) return false;
            //由于是在get时更新Caffeine,这里先执行一次进行数据更新
            return cacheFrontendContext.getCache().getIfPresent(key) != null;
        } catch (Exception e) {
            log.error("CacheUtil error, hasKey({})", key, e);
            return false;
        }
    }
}

